/****************************************************************************
 * Dirve lcd19264 , mode 8080. Tested on ABG192064C00-v1.0
 * Tested MCU
 * Owner : apleilx
 * 
 * Frequence : 40MHz
 * 
 * Some samble :
 * CN------------Chianese
 * Page----------The Page of LCD
 * Col-----------The col address of LCD
 * 16_16_________A char that is 16*16 dots
 * U-------------The chip select for LCD,(CS1--left / CS2--middle / CS3--right)
 * Com-----------LCD command
 * Disp----------Display
 * W-------------write
 * Rec-----------recently
 * Img-----------Image
 * Anti----------Anti-color
 * DIR-----------direction
 * DEL-----------delay
 * Explanation:
 * ANSEL must be set again
 * This file relay on ASCII_16_32.c,ASCII_8_16
 * that is in pic18f2xk22flash.h .
 * ASCII_16_32.c and ASCII_8_16 is unrelaied on hardware ,you can copy those to your project.
 * Imag.c and CN_16_16_Index.c is not necessary ,unless you want to display chianese and image.
 * **************************************************************************/
#include "common.h"
#include "pincfg.h"

void Lcd_WriteCom_CS1(unsigned char Content);
void Lcd_WriteCom_CS2(unsigned char Content);
void Lcd_WriteCom_CS3(unsigned char Content);
void LcdScreenFill(unsigned char Data);
void LcdHalInit(void);

#define LCD_CS 3

/**************************************************************************
 * define control bit for Lcd
 *************************************************************************/

#define LCD_BUSY_MASK ((unsigned char)0x80)

#define Lcd_CS1_Enable() ClrBit(GPIOE->ODR, 4)
#define Lcd_CS1_Disable() SetBit(GPIOE->ODR, 4)

#define Lcd_CS2_Enable() ClrBit(GPIOE->ODR, 0)
#define Lcd_CS2_Disable() SetBit(GPIOE->ODR, 0)

#define Lcd_CS3_Enable() ClrBit(GPIOD->ODR, 0)
#define Lcd_CS3_Disable() SetBit(GPIOD->ODR, 0)

#define Lcd_E(x) (x) ? (SetBit(GPIOI->ODR, 0)) : (ClrBit(GPIOI->ODR, 0))
#define Lcd_RW(x) (x) ? (SetBit(GPIOD->ODR, 3)) : (ClrBit(GPIOD->ODR, 3))
#define Lcd_RS(x) (x) ? (SetBit(GPIOD->ODR, 4)) : (ClrBit(GPIOD->ODR, 4))
#define Lcd_RESET(x) (x) ? (SetBit(GPIOE->ODR, 3)) : (ClrBit(GPIOE->ODR, 3))

void Lcd_DATA_Write(uint8_t Data)
{
    GPIOG->DDR = 0xFF;
    GPIOG->CR2 = 0xFF;
    GPIOG->ODR = Data;
}

uint8_t Lcd_BUSY()
{
    uint8_t StBusy;
    GPIOG->CR2 = 0x00;
    GPIOG->DDR = 0x00;
    NOP10();
    StBusy = GPIOG->IDR & 0x80;
    return StBusy;
}

/*****************************************************************************
 *lcd E delay , 450ns
 ****************************************************************************/
#define lcd_e_dly() NOP5()
#define  lcd_ctr_dly() NOP10()
#define  lcd_dat_dly() NOP5()


/*****************************************************************************
 *lcd Ready delay , 350ns 
 ****************************************************************************/
void lcd_read_dly()
{
    NOP8();
}

void Lcd_RESET_DEL()
{
    NOP10();
    NOP10();
    NOP10();
    NOP10();
    NOP10();
    NOP10();
    NOP10();
    NOP10();
    NOP10();
    NOP10();
    NOP10();
    NOP10();
    NOP10();
    NOP10();
    NOP10();
}

/******************************************************************************
 * Init
 * @param Content
 *****************************************************************************/
void LcdHalInit(void)
{
    Lcd_CS1_Disable();
    Lcd_CS2_Disable();
    Lcd_CS3_Disable();
    Lcd_E(0);

    Lcd_RW(0);
    Lcd_RS(0);

    DBIn();

    Lcd_RESET(0);
    Lcd_RESET_DEL();
    Lcd_RESET(1);
    Lcd_RESET_DEL();
    Lcd_RESET(0);
    Lcd_RESET_DEL();
    Lcd_RESET(1);

    DBOutEn();

    //0x3E - display OFF
    Lcd_WriteCom_CS1(0x3E);
    Lcd_WriteCom_CS2(0x3E);
    Lcd_WriteCom_CS3(0x3E);
    //0x3F - display ON
    Lcd_WriteCom_CS1(0x3F);
    Lcd_WriteCom_CS2(0x3F);
    Lcd_WriteCom_CS3(0x3F);
    //set start line
    Lcd_WriteCom_CS1(0xc0);
    Lcd_WriteCom_CS2(0xc0);
    Lcd_WriteCom_CS3(0xc0);

    LcdScreenFill(0x00);
    //LcdDispAnti();
}

/******************************************************************************
 * Waiting LCD untile it is not busy
 * @param Content
 *****************************************************************************/
void Lcd_Busy()
{
    unsigned short WaitTimeOutCnt = 0;

    Lcd_RS(0); //read status reg
    Lcd_RW(1);
    lcd_ctr_dly();
    Lcd_E(1);
    lcd_read_dly();

    while (Lcd_BUSY() != 0)
    {
        if ((WaitTimeOutCnt++) > 500)
            break;
    }

    Lcd_E(0);
}

/******************************************************************************
 * Write command to lcd
 * @param Content
 *****************************************************************************/
void Lcd_ComW(unsigned char Command)
{
    Lcd_Busy();
    Lcd_RS(0); //com
    Lcd_RW(0); //write
    Lcd_DATA_Write(Command);

    Lcd_E(1);
    lcd_e_dly();
    Lcd_E(0);
	lcd_e_dly();
}

/******************************************************************************
 * Write data to lcd
 * @param Content
 *****************************************************************************/
void Lcd_DataW(unsigned char Data)
{
    Lcd_Busy();
    Lcd_RS(1); //data
    Lcd_RW(0); //write
    Lcd_DATA_Write(Data);

    Lcd_E(1);
    lcd_e_dly();
    Lcd_E(0);
	lcd_e_dly();
}

/******************************************************************************
 * Write command to CS1
 * @param Content
 *****************************************************************************/
void Lcd_WriteCom_CS1(unsigned char Content)
{
    Lcd_CS2_Disable();
    Lcd_CS3_Disable();
    Lcd_CS1_Enable();

    Lcd_ComW(Content);
}

/******************************************************************************
 * Write command to CS2
 * @param Content
 *****************************************************************************/
void Lcd_WriteCom_CS2(unsigned char Content)
{
    Lcd_CS1_Disable();
    Lcd_CS3_Disable();
    Lcd_CS2_Enable();

    Lcd_ComW(Content);
}

/******************************************************************************
 * Write command to CS3
 * @param Content
 *****************************************************************************/
void Lcd_WriteCom_CS3(unsigned char Content)
{
    Lcd_CS1_Disable();
    Lcd_CS2_Disable();
    Lcd_CS3_Enable();

    Lcd_ComW(Content);
}

/******************************************************************************
 * Write command to CS1/CS2/CS3
 * @param Content
 *****************************************************************************/
void Lcd_WriteCom(unsigned char U, unsigned char Content)
{
    if (U == 0)
    {
        Lcd_WriteCom_CS1(Content);
    }
    else if (U == 1)
    {
        Lcd_WriteCom_CS2(Content);
    }
    else
    {
        Lcd_WriteCom_CS3(Content);
    }
}

/******************************************************************************
 * Clear the screen
 * @param Content
 *****************************************************************************/
void LcdScreenFill(unsigned char Data)
{
    unsigned char Col, Page;
    unsigned char LMR;

    for (LMR = 0; LMR < LCD_CS; LMR++)
    {
        Lcd_CS2_Disable();
        Lcd_CS3_Disable();
        Lcd_CS1_Disable();
        for (Page = 0; Page < 8; Page++)
        {
            Lcd_WriteCom(LMR, 0xB8 + Page);
            Lcd_WriteCom(LMR, 0x40);
            for (Col = 0; Col < 64; Col++)
            {
                lcd_dat_dly();
                Lcd_DataW(Data);
            }
        }
    }
}

void lcd_page_wr(uu8 block, uu8 page, unsigned char *Data)
{
    unsigned char Col, Page = page;

    Lcd_WriteCom(block, 0xB8 + Page);
    Lcd_WriteCom(block, 0x40);

    Data += block * 64;

    for (Col = 0; Col < 64; Col++)
    {
        Lcd_DataW(*Data++);
    }
}
